# This file is part of the Net::Tube module.
#
# Net::Tube is Free Software; you can use it, redistribute it and/or
# modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software
#    Foundation, either version 2, or (at your opinion) any later
#    version, or
#
# b) the "Artistic License" which comes with Perl.
#
package Net::Tube::ConfigLoader;

our $VERSION = '0.01';
our $TUBE_NS = 'http://lo-f.at/projects/net-tubes/1.0';

use XML::LibXML;
use XML::LibXML::XPathContext;

sub new {
    my $class = shift;
    my $self = bless {}, $class;
    return $self;
}

sub load_config_file {
    my $self     = shift;
    my $filename = shift;

    if ( defined $filename && -f $filename && -r $filename ) {
        my $p = XML::LibXML->new();
        my $doc;
        eval { $doc = $p->parse_file($filename); };
        if ($@) {
            $self->{tube_error} = $@;
        }
        elsif ( defined $doc->documentElement ) {
            my $cfg = $self->process_config( $doc->documentElement );
            return $self->init_tube($cfg);
        }
        else {
            $self->{tube_error} = "no config data available";
        }
    }
    return undef;
}

sub load_config_string {
    my $self     = shift;
    my $string = shift;

    if ( defined $string && -f $string && -r $string ) {
        my $p = XML::LibXML->new();
        my $doc;
        eval { $doc = $p->parse_string($string); };
        if ($@) {
            $self->{tube_error} = $@;
        }
        elsif ( defined $doc->documentElement ) {
            my $cfg = $self->process_config( $doc->documentElement );
            return $self->init_tube($cfg);
        }
        else {
            $self->{tube_error} = "no config data available";
        }
    }
    return undef;    
}

sub process_config {
    my $self = shift;
    my $node = shift;
    my $xc   = XML::LibXML::XPathContext->new();
    $xc->registerNs( 'tube', $TUBE_NS );

    my $xpath  = 'tube:tube/tube:filter';
    my $tcfg   = [];
    my @cnodes = $xc->findnodes( $xpath, $node );

    foreach my $cnode (@cnodes) {
        my $cfg = {};
        $cfg->{name}       = $cnode->getAttribute('name');
        $cfg->{parameters} = {};
        foreach my $vnode ( $cnode->childNodes ) {
            next unless $vnode->nodeType == XML_ELEMENT_NODE;
            my $name     = $vnode->localname;
            my @valueset = $vnode->findnodes('*[local-name()]');
            my $val;
            if ( scalar @valueset > 0 ) {
                $val = $vnode;
            }
            else {
                $val = $vnode->textContent;
                $val =~ s/^\s*|\s*$//g;
            }
            if ($val) {
                unless ( exists $cfg->{parameters}->{$name}
                    and ref $cfg->{parameters}->{$name} )
                {
                    $cfg->{parameters}->{$name} = [];
                }
                push @{ $cfg->{parameters}->{$name} }, $val;
            }
        }

        if ( $cfg->{name} and length $cfg->{name} ) {
            eval 'require ' . $cfg->{name} . ';';
            if ($@) {
                $self->{tube_error} = $@;
                return undef;
            }
            push @$tcfg, $cfg;
        }
    }
    return $tcfg;
}

sub init_tube {
    my $self = shift;
    my $tcfg = shift;
    my $tube;

    if ( defined $tcfg && ref $tcfg ) {
        $tube = [];
        foreach my $f (@$tcfg) {
            my $name = $f->{name};
            my $filter;
            eval '$filter = ' . $name . '->new();';
            if ($filter) {
                $filter->set_config( $f->{parameters} );
                push @$tube, $filter;
            }
            else {
                $self->{tube_error} = "cannot create a new instance of '$name'";
                return undef;
            }
        }
    }

    return $tube;
}

1;
